package com.yunxi.service.localService.impl.otd;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yunxi.bizToBaseData.service.Biz2BaseVehicleConfigDubboService;
import com.yunxi.core.service.ServiceImplPlus;
import com.yunxi.core.web.ApiResult;
import com.yunxi.core.web.ResponseUtil;
import com.yunxi.dao.otd.VehicleConfigConstraintMapper;
import com.yunxi.hubToBiz.pojo.PackageConstraintVO;
import com.yunxi.model.otd.VehicleConfigConstraint;
import com.yunxi.otd.pojo.RequiredPackagesVO;
import com.yunxi.service.localService.otd.VehicleConfigConstraintService;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

/**
 * <p>
 * 整车配置约束规则 服务实现类
 * </p>
 *
 * @author XUQI
 * @since 2019-08-05
 */
@Service
public class VehicleConfigConstraintServiceImpl extends
    ServiceImplPlus<VehicleConfigConstraintMapper, VehicleConfigConstraint> implements
    VehicleConfigConstraintService {

    @Autowired
    Biz2BaseVehicleConfigDubboService biz2baseVehicleConfigDubboService;

    /**
     * 查询所有可用的约束规则
     *
     * @return List<VehicleConfigConstraint>
     */
    @Override
    public List<VehicleConfigConstraint> findEnableConfigConstraint(String modelCode) {
        ////// 约束条件
        LambdaQueryWrapper<VehicleConfigConstraint> constraintsWrapper = new LambdaQueryWrapper<>();
        constraintsWrapper.eq(VehicleConfigConstraint::getState, "1");
        if (!StringUtils.isEmpty(modelCode)) {
            constraintsWrapper.eq(VehicleConfigConstraint::getProductId, modelCode);
        }
        return list(constraintsWrapper);
    }

    @Override
    public Map<String, List<VehicleConfigConstraint>> findEnableConfigConstraintMap() {
        //// key:车型 value:VehicleConfigConstraint
        Map<String, List<VehicleConfigConstraint>> resultMap = new HashMap<>();
        ////// 查询所有有效的约束条件
        LambdaQueryWrapper<VehicleConfigConstraint> constraintsWrapper = new LambdaQueryWrapper<>();
        constraintsWrapper.eq(VehicleConfigConstraint::getState, "1");
        List<VehicleConfigConstraint> list = list(constraintsWrapper);
        if (CollectionUtils.isEmpty(list)) {
            return resultMap;
        }
        list.forEach(constraint -> {
            List<VehicleConfigConstraint> constraints = resultMap.get(constraint.getProductId());
            if (constraints == null) {
                constraints = new ArrayList<>();
            }
            constraints.add(constraint);
            resultMap.put(constraint.getProductId(), constraints);
        });
        return resultMap;
    }

    /**
     * 根据约束关系调整默认工程配置
     *
     * @param constraints 约束关系
     * @param configMap 默认配置
     * @param allConfigMap 所有配置
     */
    @Override
    public void replaceConfig(List<VehicleConfigConstraint> constraints, Map<String, String> configMap,
        Map<String, String> allConfigMap) {
        // 是否做了修改
        boolean isModify = false;

        /////// 更新单车工程配置
        // 遍历所有约束条件
        for (VehicleConfigConstraint constraint : constraints) {
            if (constraint.getIsProcess() == 1) {
                // 已处理跳过
                continue;
            }

            // 满足约束关系前提条件
//            if (configMap.containsValue(constraint.getPremise())) {
            if (this.checkIsMeet(constraint.getPremise(), configMap)) {
                // 标记单条约束关系已处理
                constraint.setIsProcess(1);

                //结论
                String conclusion = constraint.getConclusion();

                // &条件集合
                List<String> addFeatureCodeList = new ArrayList<>(Arrays.asList(conclusion.split("&")));
                // “!”条件集合
                List<String> notList = new ArrayList<>();

                // 临时存放集合，收集二次分解中分解出来的&条件
                List<String> addList = new ArrayList<>();
                // 临时存放集合，删除“&”集合中多余项
                List<String> removeList = new ArrayList<>();

                //二次分解，从&集合中分解出“!”条件
                for (String s : addFeatureCodeList) {
                    if (s.contains("!")) {
                        String[] not = s.split("!");
                        addList.add(not[0]);// “!”前的为“&”条件
                        notList.addAll(Arrays.asList(not).subList(1, not.length));
                        removeList.add(s);
                    }
                }
                addFeatureCodeList.addAll(addList);
                addFeatureCodeList.removeAll(removeList);

                // 增加或者替换单车默认特征值
                for (String addCode : addFeatureCodeList) {
                    if (!StringUtils.isEmpty(addCode)) {
                        String replaceFamilyCode = allConfigMap.get(addCode);// 需要替换的特征族
                        if (StringUtils.isEmpty(replaceFamilyCode)) {
                            // 异常
                            throw new RuntimeException("根据特征值：" + addCode + "找不到对应的特征族");
                        }
                        if (!configMap.containsKey(replaceFamilyCode) || !configMap.containsValue(addCode)) {
                            configMap.put(replaceFamilyCode, addCode);
                            isModify = true;
                        }
                    }
                }

                // 校验“!”非条件
                for (String notCode : notList) {
                    // 需要替换的特征族
                    if (configMap.containsValue(notCode)) {
                        // 异常
                        throw new RuntimeException("互斥冲突");
                    }
                }

            }
        }

        // 如果本次循环修改了单车配置，检索是否触发其他约束规则
        if (isModify) {
            replaceConfig(constraints, configMap, allConfigMap);
        }
    }


    /**
     * BOM to PMS  Config Constraint
     *
     * @param dataList data
     * @return ApiResult
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult receiveBomData(List<PackageConstraintVO> dataList) {
        if (CollectionUtils.isEmpty(dataList)) {
            return ResponseUtil.fail(-1, "接口数据为空");
        }

        for (PackageConstraintVO pcVO : dataList) {
            //// 1.根据车型(861)删除数据
            LambdaQueryWrapper<VehicleConfigConstraint> delWrapper = new LambdaQueryWrapper<>();
            delWrapper.eq(VehicleConfigConstraint::getProductId, pcVO.getProductId());
            baseMapper.delete(delWrapper);

            //// 2.添加约束规则
            if (CollectionUtils.isEmpty(pcVO.getList())) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ResponseUtil.fail(-1, "约束数据为空");
            }
            List<VehicleConfigConstraint> constraints = new ArrayList<>();
            pcVO.getList().forEach(constraintVO -> {
                VehicleConfigConstraint constraint = new VehicleConfigConstraint();
                constraint.setPremise(constraintVO.getPremise());
                constraint.setOperator(constraintVO.getOperator());
                constraint.setConclusion(constraintVO.getConclusion());
                constraint.setDescription(constraintVO.getDescription());
                constraint.setState("1"); // 启用
                constraint.setProductId(pcVO.getProductId());
                constraint.setConfigVersion(pcVO.getVersion());
                constraint.setCreateBy("BOM");
                constraint.setCreateTime(new Date());
                constraints.add(constraint);
            });
            this.saveBatch(constraints);
        }
        return ResponseUtil.ok();
    }

    @Override
    public boolean checkIsMeet(String condition, Map<String, String> configMap) {
        return checkPriorityFormula(condition, configMap);
    }

    /**
     * 校验单车配置是否满足公式
     *
     * @param formula 公式
     * @param configMap 单车配置 Map<key:特征族,value:特征值>
     * @return 子公式
     */
    private static Boolean checkPriorityFormula(String formula, Map<String, String> configMap) {
        String priorityFormula = formula; // 需要计算的公式
        String subFormula = formula;// 子公式
        // 如果包含括号，先分解出优先执行的子公式
        if ((formula.contains("[") && formula.contains("]")) || (formula.contains("(") && formula.contains(")"))) {
            priorityFormula = getPriorityFormula(formula);
            subFormula = priorityFormula.replace("(", "").replace(")", "");
        }

        // 校验公式单车是否满足子公式
        String subResult = checkSubFormula(subFormula, configMap);
        // 用计算结果替换子公式字符串
        formula = formula.replace(priorityFormula, subResult);
        // 当公式最终替换成"true"或"false"时结束
        if ("true".equals(formula) || "false".equals(formula)) {
            return Boolean.valueOf(formula);
        }
        System.out.println(formula);
        return checkPriorityFormula(formula, configMap);
    }


    /**
     * 返回最优先执行的式子
     *
     * @param formula 公式
     * @return 子公式
     */
    private static String getPriorityFormula(String formula) {
        Stack<Character> stack = new Stack<>();
        int start = -1;
        for (int i = 0; i < formula.length(); i++) {
            switch (formula.charAt(i)) {
                case '[':
                case '(':
                    start = i;
                    stack.push(formula.charAt(i));
                    break;
                case ')':
                    //若前一个元素刚好匹配则出栈
                    if (stack.peek().toString().charAt(0) == '(') {
                        stack.pop();
                        return formula.substring(start, i + 1);
                    }
                    break;
                case ']':
                    //若前一个元素刚好匹配则出栈
                    if (stack.peek().toString().charAt(0) == '[') {
                        stack.pop();
                        return formula.substring(start, i + 1);
                    }
                    break;
            }
        }
        return formula;
    }


    /**
     * 校验公式单车是否满足公式
     *
     * @param formula 公式
     * @param configMap 单车配置
     * @return 结果 "true" or "false"
     */
    private static String checkSubFormula(String formula, Map<String, String> configMap) {
        String b = "true";
        if (formula.contains("&")) {
            List<String> and = new ArrayList<>(Arrays.asList(formula.split("&")));
            if (and.contains("false")) {
                b = "false";
            } else {
                for (String code : and) {
                    if (!"true".equals(code) && !configMap.containsValue(code)) {
                        b = "false";
                    }
                }
            }
        } else if (formula.contains("|")) {
            b = "false";
            List<String> or = new ArrayList<>(Arrays.asList(formula.split("\\|")));
            if (or.contains("true")) {
                b = "true";
            } else {
                for (String code : or) {
                    if (!"false".equals(code) && configMap.containsValue(code)) {
                        b = "true";
                    }
                }
            }
        } else if (!configMap.containsValue(formula)) {
            b = "false";
        }
        return b;
    }


    /**
     * 查询与选装包A互斥的其他选装包
     *
     * @param packages 选装包A
     * @return 互斥的其他选装包
     */
    @Override
    public ApiResult findExcludeCustomPackCode(List<String> packages) {
        if (CollectionUtils.isEmpty(packages)) {
            return ResponseUtil.ok();
        }
        return ResponseUtil.ok(baseMapper.findExcludeCustomPackCode(packages));
    }

    /**
     * 验证选装包是否正确，是否互斥
     *
     * @param packages 选装包:  XZAB,XZAC
     * @return 是否存在互斥
     */
    @Override
    public boolean checkExcludeCustomPackCode(List<String> packages, String vehicleCode) {
        if (CollectionUtils.isEmpty(packages)) {
            return true;
        }
        checkIsCustomPackCode(packages,vehicleCode);
        //   根据约束规则判断所选选装包是否存在互斥
        if (!checkExcludeCustomPackCodeByConstraint(packages)) {
            return false;
        }
        int count = baseMapper.findFamilyCodeCountByPackages(packages);
        return count == packages.size();
    }


    /**
     * 根据约束规则判断所选选装包是否存在互斥
     *
     * @param packages 选装包:  XZAB,XZAC
     * @return 是否存在互斥
     */
    @Override
    public boolean checkExcludeCustomPackCodeByConstraint(List<String> packages) {
        return baseMapper.findFamilyCodeCountByPackagesByConstraint(packages) == 0;
    }

    /**
     * 验证选装包是否正确
     *
     * @param packages 选装包
     */
    @Override
    public void checkIsCustomPackCode(List<String> packages, String vehicleCode) {
        List<String> packageDb = baseMapper.findPackages(vehicleCode);
        for (String str : packages) {
            if (!packageDb.contains(str)) {
                throw new RuntimeException(vehicleCode + "不可选择选装包：" + str);
            }
        }
    }


    /**
     * 根据 featureCode 查询必选选装包
     *
     * @param featureCode （颜色Code）
     * @return 必选选装包
     */
    @Override
    public Map<String, List<Object>> findRequiredPackagesByFeatureCode(List<String> featureCode, String vehicleCode) {
        List<RequiredPackagesVO> requiredPackagesVOS = baseMapper.findRequiredPackagesByFeatureCode(vehicleCode);
        if (CollectionUtils.isEmpty(requiredPackagesVOS)) {
            return null;
        }

        Map<String, List<Object>> map = new HashMap<>();
        // 不可单独选择的选装包
        List<Object> notSelect = new ArrayList<>();
        List<Object> requiredSelect = new ArrayList<>();
        for (RequiredPackagesVO requiredPackagesVO : requiredPackagesVOS) {
            if(!notSelect.contains(requiredPackagesVO.getPackageCode())){
                notSelect.add(requiredPackagesVO.getPackageCode());
            }
            if (featureCode.contains(requiredPackagesVO.getColorCode())) {
                requiredSelect.add(requiredPackagesVO.getPackageCode());
            }
        }
        map.put("notSelect", notSelect);
        map.put("requiredSelect", requiredSelect);
        return map;
    }

}
